/* chimaera_message_index-table.c : store message index using btree 
 *
 * the meaning of the table is mainly to accelerate the retrieve
 * speed.
 *
 * ====================================================================
 * Copyright (c) 2008-2009 Chimaera team.  All rights reserved.
 *
 *
 * @author kkndyu
 * @mail kkndyu@gmail.com
 * ====================================================================

 * key = user_recno + need_confirm + read + comfirmed(int + byte + byte + byte)
 * every message of a user in different state  will be in different queue.
 *
 * every user has five queues, as follows:
 * (in bdb it just means every user has 5 keys, and every message will
 * be stored in one of them. thanks to the btree DB_DUP mode, 
 * it allows to store many values under one key)
 *
 * user_recno    need_confirm    read    confirmed
 *
 *    num            0			  0			0   //state 1
 *    num            0			  1			0   //state 2
 *    num            1			  0			0   //state 3
 *    num            1			  1			0   //state 4
 *    num            1			  1			1   //state 5
 *
 * A message born, its state is either 1 or 3, when it is read, its state 
 * will change to 2 or 4 and it will be moved form their original queue
 * to the "read" queue. if the message need user to confirm, after the user 
 * confirmed, the state of the message will change from 4 to 5.
 *
 * When a get_new_message command come, it will only get the message in 
 * state 1, 3 and 4.
 * 
 */

#include "bdb_compat.h"
#include "svn_fs.h"
#include "svn_pools.h"
#include "../fs.h"
#include "../err.h"
#include "../util/skel.h"
#include "../util/fs_skels.h"
#include "../../libsvn_fs/fs-loader.h"
#include "bdb-err.h"
#include "dbt.h"
#include "rev-table.h"
#include "../trail.h"

#include "svn_private_config.h"
#include "private/svn_fs_util.h"

#include "chimaera_message_index-table.h"


/* Opening/creating the `chimaera_message_index' table.  */

int svn_fs_bdb__open_chimaera_message_index_table(DB **chimaerami_p,
                                     DB_ENV *env,
                                     svn_boolean_t create)
{
  const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0);
  DB *chimaere_message_index;

  BDB_ERR(svn_fs_bdb__check_version());
  BDB_ERR(db_create(&chimaere_message_index, env, 0));

  chimaere_message_index->set_flags(chimaere_message_index,DB_DUP);
 
  BDB_ERR((chimaere_message_index->open)(
						SVN_BDB_OPEN_PARAMS(chimaere_message_index, NULL),
						"chimaere_message_index", 0, DB_BTREE,
						open_flags, 0666));

  *chimaerami_p = chimaere_message_index;
  return 0;
}



/* @a num store the amount of new messages  */


svn_error_t *
svn_fs_bdb__get_chimaera_new_message(chimaeramessage_t **chimaeramessage_p,
							apr_int32_t *num, apr_int32_t user_recno, 
							svn_fs_t *fs, apr_pool_t *pool)
{
  base_fs_data_t *bfd = fs->fsap_data;
  int db_err;
  DBT key, value;
  DBC *cursor = 0;
  
  if (user_recno>0)
  {
	  chimaeramessage_t * cm = apr_palloc(pool,sizeof(*cm));
	  int messnum1=0,messnum2=0;
	  chimaeramessage_t *temp,*firstm;

	  /* change state should del the original m then add a new one
	   * this should a atom operation, so txn ...
	   */
	  DB_TXN *txn;
	  bfd->bdb->env->txn_begin(bfd->bdb->env,NULL,&txn,0);

	  SVN_ERR(BDB_WRAP(fs, "getting new message (creating cursor)",
		  bfd->chimaera_message_index->cursor(bfd->chimaera_message_index, txn,
		  &cursor, 0)));

	  /* first calculate all the new message nums
	   * two kinds of messages: 000 or 100
	   */
	  cm->user_recno=user_recno;
	  cm->need_confirm=0;
	  cm->read=0;
	  cm->confirmed=0;

	  svn_fs_base__set_dbt(&key, cm, CHIMAERA_MESSAGE_INDEX_KEY_LENGTH);

	  svn_fs_base__result_dbt(&value);
	  db_err = svn_bdb_dbc_get(cursor,&key,&value,DB_SET);
	  svn_fs_base__track_dbt(&value, pool);
	  /*
	   * svn_fs_base__track_dbt(&value, pool);
	   * 
	   * should pairs appearance with
	   * svn_fs_base__result_dbt(&value)
	   */
	  if (db_err != DB_NOTFOUND){
		  svn_bdb_dbc_count(cursor,&messnum1,0);				
	  }

	  cm->need_confirm=1;
	  svn_fs_base__set_dbt(&key, cm, CHIMAERA_MESSAGE_INDEX_KEY_LENGTH);

	  svn_fs_base__result_dbt(&value);
	  db_err = svn_bdb_dbc_get(cursor,&key,&value,DB_SET);
	  svn_fs_base__track_dbt(&value, pool);

	  if (db_err != DB_NOTFOUND){
		  svn_bdb_dbc_count(cursor,&messnum2,0);				
	  }

	  if (messnum1+messnum2 == 0)
	  {
		  *num = 0;
		  if (cursor)
		  {
			  SVN_ERR(BDB_WRAP(fs, "getting youngest revision (closing cursor)",
				  svn_bdb_dbc_close(cursor)));
		  }
		  txn->commit(txn,0);
		  return SVN_NO_ERROR;
	  }
	  
	  /* use the num just calculated to malloc memories*/
	  temp = apr_palloc(pool,(messnum1+messnum2)*sizeof(*temp));
	  firstm = temp;

	  /* after using count function,which record will the cursor point to?
	   * the last or the first?
	   * 
	   * after calculate, malloc the memory to store the messages
	   */
	  if (messnum2!=0)
	  {
		  svn_fs_base__result_dbt(&value);
		  db_err = svn_bdb_dbc_get(cursor,&key,&value,DB_SET);
		  svn_fs_base__track_dbt(&value, pool);

		  memcpy(&(temp->message_id),value.data,value.size);

		  /* write the message in the return pointer*/
		  temp->user_recno=user_recno;
		  temp->need_confirm=1;
		  temp->confirmed=0;
		  
		  db_err = svn_bdb_dbc_del(cursor,0);				
		  temp->read=1;
		  temp->read_time = apr_time_now();
		  svn_fs_bdb__put_chimaera_message_index(temp,fs,txn,pool);

		  temp++;
		  

		  while((db_err = svn_bdb_dbc_get(cursor,
			  &key,svn_fs_base__result_dbt(&value),DB_NEXT_DUP))==0)
		  {
			  svn_fs_base__track_dbt(&value, pool);
			  memcpy(&(temp->message_id),value.data,value.size);

			  temp->user_recno=user_recno;
			  temp->need_confirm=1;
			  temp->confirmed=0;

			  /* change the message state from unread to read
			   * delete then add.
			   */
              db_err = svn_bdb_dbc_del(cursor,0);				
			  temp->read=1;
			  temp->read_time = apr_time_now();
			  svn_fs_bdb__put_chimaera_message_index(temp,fs,txn,pool);

			  temp++;
		  }
		 
	  }

	  if (messnum1!=0)
	  {
		  cm->need_confirm=0;
		  svn_fs_base__set_dbt(&key, cm, CHIMAERA_MESSAGE_INDEX_KEY_LENGTH);
		  svn_fs_base__result_dbt(&value);
		  db_err = svn_bdb_dbc_get(cursor,&key,&value,DB_SET);
		  svn_fs_base__track_dbt(&value, pool);

		  memcpy(&(temp->message_id),value.data,value.size);

		  temp->user_recno=user_recno;
		  temp->need_confirm=0;
		  temp->confirmed=0;

		  db_err = svn_bdb_dbc_del(cursor,0);				
		  temp->read=1;
		  temp->read_time = apr_time_now();
		  svn_fs_bdb__put_chimaera_message_index(temp,fs,txn,pool);

		  temp++;

		  while((db_err = svn_bdb_dbc_get(cursor,
			  &key,svn_fs_base__result_dbt(&value),DB_NEXT_DUP))==0)
		  {
			  svn_fs_base__track_dbt(&value, pool);
			  memcpy(&(temp->message_id),value.data,value.size);

			  temp->user_recno=user_recno;
			  temp->need_confirm=0;
			  temp->confirmed=0;

			  /* change the message state from unread to read
			  * delete then add.
			  */
			  db_err = svn_bdb_dbc_del(cursor,0);				
			  temp->read=1;
			  temp->read_time = apr_time_now();
			  svn_fs_bdb__put_chimaera_message_index(temp,fs,txn,pool);

			  temp++;
		  }

	  }

	  /* befor commit , close the cursor ...*/
	  if (cursor)
	  {
		  SVN_ERR(BDB_WRAP(fs, "getting new message (closing cursor)",
			  svn_bdb_dbc_close(cursor)));
	  }

	  txn->commit(txn,0);

	  *chimaeramessage_p = firstm;
	  *num = messnum1 + messnum2;
	  
  }
  
  

  return SVN_NO_ERROR;
}


svn_error_t *
svn_fs_bdb__put_chimaera_message_index(chimaeramessage_t *chimaeramessage_p,
							  svn_fs_t *fs, DB_TXN *txn,apr_pool_t *pool)
{
	base_fs_data_t *bfd = fs->fsap_data;
	DBT key, value;
	void * value_pointer;

	svn_fs_base__set_dbt(&key, chimaeramessage_p, CHIMAERA_MESSAGE_INDEX_KEY_LENGTH);

	/* move the pointer to point at the value section in the structure*/
	value_pointer = &(chimaeramessage_p->message_id);

	svn_fs_base__set_dbt(&value, value_pointer, CHIMAERA_MESSAGE_INDEX_VALUE_LENGTH);

	SVN_ERR(BDB_WRAP(fs, _("storing message index"),
		bfd->chimaera_message_index->put(bfd->chimaera_message_index, txn,
						&key, &value, 0)));
	
	
	return SVN_NO_ERROR;
}

/* using cursor, remove original then add to new queue, in logical the retrieve
* key is chimaeramessage_p->message_id. But the chimaeramessage_p->user_recno can 
* help to make the retrieve scope smaller.
*/
svn_error_t *
svn_fs_bdb__update_chimaera_message_state(chimaeramessage_t *chimaeramessage_p,
									   svn_fs_t *fs, DB_TXN *txn,apr_pool_t *pool)
{
	return SVN_NO_ERROR;
}
